#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "global.h"
#include "fs.h"
#include "task.h"

//#define MAX_HEIGHT	10000
//#define MAX_WIDTH 	10000

//G_List *gell	= NULL;
//E_List *iell	= NULL;
//E_List *oell	= NULL;
/* int REJECTED_TASK	= 0;
int WAITING_TIME	= 0;
int RUNNING_TASK	= 0;
int NO_OF_MER		= 0;
int MAX_HEIGHT		= 0;
 *//* int MAX_WIDTH		= 0; */

G_Node *gnew = (G_Node *)NULL;
E_Node *ienew = (E_Node *)NULL;
E_Node *oenew = (E_Node *)NULL;

Task_List *incoming_tasks = NULL;
Task_List *tasks = NULL;
Task_List *queue = NULL;

G_Node * initial_g (int height, int ie_count, int oe_count, G_Node *prev) {
	G_Node *ge = (G_Node *) malloc (sizeof (G_Node));
	ge->height = height;
	ge->ie_count = ie_count;
	ge->oe_count = oe_count;
	ge->next = NULL;
	ge->prev = prev;
	if (prev != NULL) {
		printf("prev h:%d\n",prev->height);
		if (prev->next != NULL) {
			printf("next h:%d\n", prev->next->height);
		}
		ge->next = prev->next;
		prev->next = ge;
	}
	return ge;
}

E_Node * initial_e (int height, int x_left, int x_right, G_Node *gnode, E_Node *prev) {
	E_Node *enode = (E_Node *) malloc(sizeof(E_Node));
	enode->height	= height;
	enode->x_left	= x_left;
	enode->x_right	= x_right;
	enode->gnode	= gnode;
	enode->next	= NULL;
	enode->prev	= prev;
	if (prev != NULL) {
		enode->next = prev->next;
		prev->next = enode;
		printf("update list\n");
	}
	return enode;
}

void initial (G_List *gell, E_List *iell, E_List *oell) {
	G_Node *ge;
	E_Node *ie, *oe;

	ge = initial_g(0,0,1,NULL);
	gell->head = ge;
	oe = initial_e(0,0,MAX_WIDTH,ge,NULL);
	oell->head = oe;

	ge = initial_g(MAX_HEIGHT,1,0,gell->head);
	gell->head->next = ge;
	ie = initial_e(MAX_HEIGHT,0,MAX_WIDTH,ge,NULL);
	iell->head = ie;
}

MER * find_position_ff(Task_Node *task, M_List *mer) {
	if (mer == NULL) return NULL;

	MER *current = mer->head;
	while (current != NULL) {
		int width = current->right - current->left;
		int height = current->top - current->bottom;
//printf("%d %d %d %d\n",task->width, width, task->height, height);
		if ((task->width <= width) && (task->height <= height)) {
			break;
		}
		current = current->next;
	}
	return current;
}

MER * find_position_bf(Task_Node *task, M_List *mer) {
	if (mer == NULL) return NULL;

	MER *best = NULL;
	MER *current = mer->head;
	int space_left = MAX_HEIGHT * MAX_WIDTH;
	int task_size = task->width * task->height;
	while (current != NULL) {
		int width = current->right - current->left;
		int height = current->top - current->bottom;
		if ((task->width <= width) && (task->height <= height)) {
			int size = width * height;
			if (space_left > (size - task_size)) {
				best = current;
				space_left = size - task_size;
			}
		}
		current = current->next;
	}
	return best;
}

Task_Node * create_task (int arrival_time, int execution_time, int deadline_time, int width, int height) {
	Task_Node *t_new = (Task_Node *)malloc(sizeof(Task_Node));
	t_new->arrival_time	= arrival_time;
	t_new->execution_time	= execution_time;
	t_new->deadline_time	= deadline_time;
	t_new->width		= width;
	t_new->height		= height;
	return t_new;
}

void task_placement(int sim_t, char select_type) {
	Task_Node *t_current = NULL;
	M_List *merll = NULL;
	MER *mer = NULL;
	int wait = 0;

	t_current = queue->head;
	while (t_current->arrival_time <= sim_t && !wait) {
//		print_t(t_current);
		if (sim_t + t_current->execution_time > t_current->deadline_time) {
            printf("task_placement: sim_t:%2d REJECT TASK ", sim_t);
            print_t(t_current);
			delete_task(&queue, t_current, NULL);
			REJECTED_TASK++;
		} else {
//			printf("simulate time %d\n",sim_t);
/*   				printf("xxxxxxxxxxxxxxxxx BEFORE INSERT xxxxxxxxxxxxxxxxxxxxxx\n");
                print_glist(ghead);	
                printf("xxxxxxxxxxxxxxxxx    IN EDGE    xxxxxxxxxxxxxxxxxxxxxx\n");
                print_elist(iehead);  
                printf("xxxxxxxxxxxxxxxxx    OUT EDGE   xxxxxxxxxxxxxxxxxxxxxx\n");
                print_elist(oehead); */
			merll = fs_algorithm(ghead, iehead, oehead);
//			print_merll(merll);
			if (select_type == 'f') 
				mer	= find_position_ff(t_current, merll);
			else
				mer	= find_position_bf(t_current, merll);

			if (mer != NULL) {
                WAITING_TIME = WAITING_TIME + (sim_t - t_current->arrival_time);
				printf("task_placement: sim_t:%2d insert @ x:%2d y:%2d ", sim_t, mer->left, mer->bottom);
				print_t(t_current);

				gnew = init_gnode(mer->bottom,1,0);
				insert_gnode(gnew);
				ienew = init_enode(mer->bottom, mer->left, mer->left + t_current->width,gnew);
				insert_enode(ienew,'i');
		
				gnew = init_gnode(mer->bottom + t_current->height,0,1);
				insert_gnode(gnew);
				oenew = init_enode(mer->bottom + t_current->height, mer->left, mer->left + t_current->width,gnew);
				insert_enode(oenew,'o');
		
				t_current->ienode = ienew;
				t_current->oenode = oenew;

				delete_task(&queue, t_current, NULL);
				add_task(&tasks, t_current);
				RUNNING_TASK++;
/* 				printf("sim_t %d --- after add task -----------------------><-------------------\n",sim_t);
				print_glist(ghead);	
                printf("============================ out edge \n");
                print_elist(oehead);
                printf("++++++++++++++++++++++++++++ in edge \n");
                print_elist(iehead); */
/*   				printf("xxxxxxxxxxxxxxxxx AFTER TASK  xxxxxxxxxxxxxxxxxxxxxx\n");
                print_glist(ghead);	
                printf("xxxxxxxxxxxxxxxxx   IN EDGE   xxxxxxxxxxxxxxxxxxxxxx\n");
                print_elist(iehead);  
                printf("xxxxxxxxxxxxxxxxx   OUT EDGE  xxxxxxxxxxxxxxxxxxxxxx\n");
                print_elist(oehead); */
				merll = fs_algorithm(ghead, iehead, oehead);
				print_merll(merll);
				NO_OF_MER += merll_size(merll);
				getchar();
			} else {
//				WAITING_TIME++;
				wait = 1;
			}
		}
		if (queue == NULL) return;
		t_current = queue->head;
	}
}

int main(int argc, char *argv[]) {
	if (argc != 6) { 
		printf("%s filename [f|s] [f|b] w h\n", argv[0]);
		return -1; 
	}

//	M_List *merll	= NULL;
//	MER *mer	= NULL;

	FILE *fp;
	Task_Node *task = NULL;
	Task_Node *new_task = NULL;
//	Task_Node *processing_task = NULL;
//	Task_Node *t_current = NULL;
//	Task_List *tasks = NULL;
//	Task_List *queue = NULL;
	char *filename = argv[1];
	char select_queue = argv[2][0];
	char select_type = argv[3][0];
	int max_width = atoi(argv[4]);
	int max_height = atoi(argv[5]);
	MAX_WIDTH = max_width;
	MAX_HEIGHT = max_height;
//	char *filename = "c:\\temp\\test.txt";

	fp = fopen(filename, "r");
	if (fp == NULL) {
		printf("open_file %s",strerror(errno));
/* 		char *error = strerror(errno); */
		getchar();
		return -1;
	}
	while(!feof(fp)) {
		task = NULL;
		read_task(&task, fp);
		add_task(&incoming_tasks, task);
//		add_task(&queue, task);
	}
	fclose(fp);

//	G_Node *gnew = (G_Node *)NULL;
//	E_Node *ienew = (E_Node *)NULL;
//	E_Node *oenew = (E_Node *)NULL;

	gnew = init_gnode(0,0,1);
	insert_gnode(gnew);
	
	oenew = init_enode(0,0,MAX_WIDTH,gnew);
	insert_enode(oenew,'o');
	
	gnew = init_gnode(MAX_HEIGHT,1,0);
	insert_gnode(gnew);

	ienew = init_enode(MAX_HEIGHT,0,MAX_WIDTH,gnew);
	insert_enode(ienew,'i');

/*                 print_glist(ghead);	
                printf("============================ out edge \n");
                print_elist(oehead);
                printf("++++++++++++++++++++++++++++ in edge \n");
                print_elist(iehead); */
	int sim_t = 0;

    while ((incoming_tasks != NULL) || (queue != NULL) || (tasks != NULL)) {
//        printf("simulation time %d ------------------------------\n",sim_t);
        if (incoming_tasks != NULL) {
            new_task = incoming_tasks->head;
            if (new_task->arrival_time <= sim_t) {
                if (new_task->next != NULL) {
                    incoming_tasks->head = new_task->next;
                    new_task->next = NULL;
                } else {
                    free(incoming_tasks);
                    incoming_tasks = NULL;
                } 

                if (select_queue == 'f') 
                    schedule_FCFS(&queue, new_task);
                else
                    schedule_SJF(&queue, new_task);

//                print_task2(queue);
            }
        }

 		Task_List *remove = NULL;
		reduce_time(&tasks, &remove);
		if (remove != NULL) {
//            print_task2(remove);
			Task_Node *temp = remove->head;
			while (temp != NULL) {
                printf("main: sim_t:%2d remove ", sim_t);
                print_t(temp);
//                print_elist(temp->oenode);
				delete_enode(temp->oenode,'o');
				delete_enode(temp->ienode,'i');
				temp = temp->next;
/*   				printf("xxxxxxxxxxxxxxxxx REMOVE TASK xxxxxxxxxxxxxxxxxxxxxx\n");
                print_glist(ghead);	
                printf("xxxxxxxxxxxxxxxxx   IN EDGE   xxxxxxxxxxxxxxxxxxxxxx\n");
                print_elist(iehead);  
                printf("xxxxxxxxxxxxxxxxx   OUT EDGE  xxxxxxxxxxxxxxxxxxxxxx\n");
                print_elist(oehead); */
			}
		} 
        
/*         if ((processing_task == NULL) && (queue != NULL)) { */
/*         if (queue != NULL) {
            processing_task = queue->head;
            
            if (processing_task->next !=NULL) {
                queue->head = processing_task->next;
                processing_task->next = NULL;
            } else {
                free(queue);
                queue = NULL;
            }
        }  */
        
 		if (queue != NULL) {
			task_placement(sim_t, select_type); 
		} 
        

/*         if (processing_task == NULL) break;
            
        printf("%d, %d\n",processing_task->arrival_time, sim_t);
        if (processing_task->arrival_time > sim_t) {
            print_t(processing_task);
            if (select_type == 'f') 
                schedule_FCFS(&queue, processing_task);
            else
                schedule_SJF(&queue, processing_task);
        } else { 
            if (processing_task->deadline_time > (sim_t + processing_task->execution_time)) {
                REJECTED_TASK++;
            }
        }  */
        
//        processing_task = NULL;
        sim_t++;
    }
//    print_task2(queue);

/* 	while (queue != NULL || tasks != NULL) {
		Task_List *remove = NULL;
		reduce_time(&tasks, &remove);
		if (remove != NULL) {
			Task_Node *temp = remove->head;
			while (temp != NULL) {
				delete_enode(temp->oenode,'o');
				delete_enode(temp->ienode,'i');
				temp = temp->next;
			}
		}

		if (queue != NULL) {
			task_placement(sim_t, select_type); 
		}
		
		sim_t++;
	} */
	printf("%5d\t", max_height * max_width);
	printf("%d\t", NO_OF_MER);
	printf("AE: %5.2f\t", NO_OF_MER*1.0/RUNNING_TASK);
	int total_task = RUNNING_TASK + REJECTED_TASK;
	printf("PAT: %3.2f\t", RUNNING_TASK*100.0 / total_task);
	printf("AWT: %5.4f\tRejected %d\tRunning %d\n", WAITING_TIME*1.0/RUNNING_TASK, REJECTED_TASK, RUNNING_TASK); 
    getchar();
	return 0;
}
